﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using SOWY.common;

///testing
using System.Data.Odbc;
using System.IO;

namespace SOWY
{
    public partial class MainForm : Form
    {
        private KeywordProcessing objKeywordProcessing;
        private LogOperation objLogOperation;
        private DataOperations objDataOperation;
        private List<List<Task>> performedTasks;
        private GUILogic objGUILogic;

        public MainForm()
        {
            objDataOperation = new DataOperations();
            objLogOperation = new LogOperation();
            performedTasks = new List<List<Task>>();

            InitializeComponent();
            objGUILogic = GUILogic.getInstance();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            //this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; 
            setupAutoComplete();
            loadHistory();
            loadAgenda();
            setFocusOnTxtCommand();
        }

        private void setFocusOnTxtCommand()
        {
            txtCommand.TabIndex = 0;
            txtCommand.Focus();
        }
        private void setupAutoComplete()
        {
            txtCommand.AutoCompleteMode = AutoCompleteMode.Suggest;
            txtCommand.AutoCompleteSource = AutoCompleteSource.CustomSource;
            txtCommand.AutoCompleteCustomSource.AddRange(GeneralConstant.KEY_POSSIBLE_CREATE);
            txtCommand.AutoCompleteCustomSource.AddRange(GeneralConstant.KEY_POSSIBLE_DELETE);
            txtCommand.AutoCompleteCustomSource.AddRange(GeneralConstant.KEY_POSSIBLE_UPDATE);
            txtCommand.AutoCompleteCustomSource.AddRange(GeneralConstant.KEY_POSSIBLE_RETRIEVE);
            txtCommand.AutoCompleteCustomSource.AddRange(GeneralConstant.KEY_POSSIBLE_UNDO);
            txtCommand.AutoCompleteCustomSource.AddRange(GeneralConstant.KEY_POSSIBLE_EXIT);

        }
        private void loadAgenda()
        {
            List<Task> tasks = getAllTasks();
            int top = GeneralConstant.DESIGN_AGENDA_TOP;
            pnlAgenda.Controls.Clear();

            if (!objGUILogic.isError(tasks))
                for (int lineNum = 0; lineNum < tasks.Count; lineNum++)
                {
                    setupDateLabel(lineNum, ref top, tasks[lineNum].startDate.ToShortDateString(), tasks[lineNum].endDate.ToShortDateString());
                    setupTaskLabel(ref top, tasks[lineNum].task, tasks[lineNum].status);
                }
        }
        private void loadHistory()
        {
            DataOperations dataOper = new DataOperations();
            LogOperation logOp = new LogOperation();
            DataTable dt = logOp.readLog();
            int numberOfRow = dt.Rows.Count;
            int startCount = 0;
            if (numberOfRow == 0)
            {
                txtInfo.AppendText(GeneralConstant.DESIGN_NEW_USER);
                return;
            }
            txtInfo.AppendText(GeneralConstant.DESIGN_HISTORY_START);
            if (numberOfRow > GeneralConstant.DESIGN_AGENDA_HISTORY_MAX_COUNT)
                startCount = numberOfRow - GeneralConstant.DESIGN_AGENDA_HISTORY_MAX_COUNT;

            for (int i = startCount; i < numberOfRow; i++)
            {
                DataRow dr = dt.Rows[i];
                string action = dr[GeneralConstant.DATABASE_LOG_COLUMN_INDEX_ACTION].ToString();
                string task = dr[GeneralConstant.DATABASE_LOG_COLUMN_INDEX_TASK].ToString();
                string startDate = dr[GeneralConstant.DATABASE_LOG_COLUMN_INDEX_START_DATE].ToString();
                string endDate = dr[GeneralConstant.DATABASE_LOG_COLUMN_INDEX_END_DATE].ToString();
                Task objTask = new Task(task, DateTime.Parse(startDate), DateTime.Parse(endDate), action);
                string line = "[" + objTask.type + "] " + objGUILogic.processTask(objTask) + "\n";
                txtInfo.AppendText(line);
            }
            txtInfo.AppendText(GeneralConstant.DESIGN_HISTORY_END);
        }

        private List<Task> getAllTasks()
        {
            objKeywordProcessing = new KeywordProcessing();
            List<Command> list = new List<Command>();
            list.Add(new Command(GeneralConstant.KEY_RETRIEVE));
            List<Task> tasks = objDataOperation.toDatabase(list);
            return tasks;
        }
        private string getDateLabelText(string startDate, string endDate)
        {
            string text;
            string minValue = DateTime.MinValue.ToShortDateString();
            if (startDate.Equals(minValue) && endDate.Equals(minValue))
            {
                text = GeneralConstant.DESIGN_AGENDA_TO_DO_STRING;
            }
            else if (startDate.Equals(minValue))
            {
                text = endDate;
            }
            else if (endDate.Equals(minValue))
            {
                text = startDate;
            }
            else
            {
                text = startDate + " -- " + endDate;
            }
            return text;
        }
        private void setupTaskLabel(ref int top, string task, string status)
        {
            Label objLabelTask = new Label();
            objLabelTask.AutoSize = true;
            objLabelTask.BackColor = System.Drawing.Color.Transparent;
            objLabelTask.Font = new System.Drawing.Font(GeneralConstant.DESIGN_AGENDA_FONT_FAMILY, GeneralConstant.DESIGN_AGENDA_TASK_FONT_SIZE, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            objLabelTask.ForeColor = System.Drawing.SystemColors.Control;
            objLabelTask.Location = new System.Drawing.Point(GeneralConstant.DESIGN_AGENDA_TASK_LEFT, top);

            objLabelTask.Text = task + " - " + status;
            pnlAgenda.Controls.Add(objLabelTask);
            top += objLabelTask.Size.Height + GeneralConstant.DESIGN_AGENDA_TASK_MARGIN;
        }
        private void setupDateLabel(int lineNum, ref int top, string startDate, string endDate)
        {
            Label objLabelDate = new Label();
            objLabelDate.Text = getDateLabelText(startDate, endDate);
            if (!(lineNum > GeneralConstant.TO_DO_TASK && objLabelDate.Text.Equals(GeneralConstant.DESIGN_AGENDA_TO_DO_STRING)))
            {
                objLabelDate.AutoSize = true;
                objLabelDate.BackColor = System.Drawing.Color.Transparent;
                objLabelDate.Font = new System.Drawing.Font(GeneralConstant.DESIGN_AGENDA_FONT_FAMILY, GeneralConstant.DESIGN_AGENDA_DATE_FONT_SIZE, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
                objLabelDate.ForeColor = System.Drawing.SystemColors.ControlLight;
                objLabelDate.Location = new System.Drawing.Point(GeneralConstant.DESIGN_AGENDA_DATE_LEFT, top);
                //objLabelDate.Name = "lblDate";
                pnlAgenda.Controls.Add(objLabelDate);
                top += objLabelDate.Size.Height;
            }
            else
            {
                top -= GeneralConstant.DESIGN_AGENDA_TASK_MARGIN;
            }
        }

        private void processCommand()
        {
            objKeywordProcessing = new KeywordProcessing();
            List<Command> commandList = objKeywordProcessing.processKeyword(txtCommand.Text.Trim());
            List<Task> list_feedback = objDataOperation.toDatabase(commandList);
            DisplayOutputOnInfoPanel(list_feedback);
        }
        private void DisplayOutputOnInfoPanel(List<Task> list_feedback)
        {
            string output = "";
            if (objGUILogic.isError(list_feedback))
            {
                output = objGUILogic.getErrorMessage(list_feedback);
            }
            else
            {
                output = getFeedbackMessageAndLogging(list_feedback);
            }
            writeOutputOnInfoPanelAndClearCommand(output);
        }
        private string getFeedbackMessageAndLogging(List<Task> list_feedback)
        {
            string output = "";
            List<Task> taskList = new List<Task>();
            for (int i = 0; i < list_feedback.Count; i++)
            {
                Task task = list_feedback[i];
                if (GeneralConstant.KEY_ADD.Equals(task.type) || GeneralConstant.KEY_DELETE.Equals(task.type))
                {
                    taskList.Add(task);
                    objLogOperation.insertLog(task.type, task.task, task.startDate.ToShortDateString(), task.endDate.ToShortDateString());
                }
                else if (GeneralConstant.KEY_UPDATE.Equals(task.type))
                {
                    objLogOperation.insertLog(task.type, task.task, task.startDate.ToShortDateString(), task.endDate.ToShortDateString());
                    taskList.Add(task);
                    i++;
                    //defensive coding, prevent wrong assumption
                    //Debug.Assert(i >= list_feedback.Count);
                    task = list_feedback[i];
                    taskList.Add(task);
                }
                output += "[" + task.type + "] " + objGUILogic.processTask(task) + "\n";

            }
            performedTasks.Add(taskList);
            return output;
        }
        private void writeOutputOnInfoPanelAndClearCommand(string output)
        {
            txtInfo.AppendText(output);
            txtInfo.ScrollToCaret();
            txtCommand.Clear();
        }

        private void showHelp()
        {
            MessageBox.Show(GeneralConstant.INFO_HELP, "Help", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        private void maximiseForm()
        {
            this.Show();
            this.WindowState = FormWindowState.Normal;
        }
        private void minimiseForm()
        {
            notifyIcon.ShowBalloonTip(500);
            this.Hide();
        }

        private void MainForm_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Q && e.Modifiers == Keys.Control)
            {
                minimiseForm();
            }
            else if (e.KeyCode == Keys.Z && e.Modifiers == Keys.Control)
            {
                undoAction();
                loadAgenda();
            }
            else if (e.KeyCode == Keys.F1)
            {
                showHelp();
            }

        }
        private void txtCommand_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                string command = txtCommand.Text.ToLower().Trim();
                if (objGUILogic.isEmptyString(command))
                {
                    return;
                }
                if (objGUILogic.checkExitCommand(command))
                {
                    Environment.Exit(0);
                }
                else if (objGUILogic.checkUndoCommand(command))
                {
                    undoAction();
                }
                else if (objGUILogic.checkUpdateCommand(command))
                {
                    updateAction();
                }
                else if (objGUILogic.checkDeleteCommand(command))
                {
                    deleteAction();
                }
                else
                {
                    processCommand();
                }
                loadAgenda();
            }
        }
        private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            maximiseForm();
        }

        private void undoAction()
        {
            if (performedTasks.Count == 0)
            {
                MessageBox.Show(GeneralConstant.INFO_UNDO, "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                List<Task> taskList = performedTasks[performedTasks.Count - 1];
                List<Command> commandList = new List<Command>();
                string output = "";
                foreach (Task objTask in taskList)
                {
                    string action = objTask.type;
                    string task = objTask.task;
                    DateTime startDate = objTask.startDate;
                    DateTime endDate = objTask.endDate;
                    if (action.Equals(GeneralConstant.KEY_ADD))
                    {
                        commandList.Add(new Command(task, GeneralConstant.KEY_DELETE, startDate, endDate));

                    }
                    else if (action.Equals(GeneralConstant.KEY_DELETE))
                    {
                        commandList.Add(new Command(task, GeneralConstant.KEY_ADD, startDate, endDate));
                    }

                    objLogOperation.deleteLog();
                    output += action + " [" + task + "] - " + GeneralConstant.INFO_ACTION_CANCEL
                        + "\n";
                }
                objDataOperation.toDatabase(commandList);
                writeOutputOnInfoPanelAndClearCommand(output);
                performedTasks.RemoveAt(performedTasks.Count - 1);
            }
        }
        private void deleteAction()
        {
            List<Task> tasks = getAllTasks();
            if (objGUILogic.isError(tasks))
            {
                DisplayOutputOnInfoPanel(tasks);
                return;
            }

            string input = InputDialog.show(tasks, GeneralConstant.KEY_DELETE);
            if (objGUILogic.isEmptyString(input))
            {
                return;
            }

            List<Command> list = new List<Command>();
            string errorMessage = processDeleteCommand(tasks, input, list);

            objGUILogic.showErrorMessageBox(errorMessage);

            if (!objGUILogic.isEmptyList(list))
            {
                List<Task> feedback = objDataOperation.toDatabase(list);
                DisplayOutputOnInfoPanel(feedback);
                loadAgenda();
            }

        }

        private string processDeleteCommand(List<Task> tasks, string input, List<Command> list)
        {
            string[] indexes = input.Split(GeneralConstant.DELIMITER_COMMA);
            string errorMessage = "";

            foreach (string str_index in indexes)
            {
                int index;
                bool result = Int32.TryParse(str_index, out index);
                if (result)
                {
                    if (objGUILogic.isIndexInRange(tasks, index))
                    {
                        Task t = tasks[index - 1];
                        list.Add(new Command(t.task, GeneralConstant.KEY_DELETE, t.startDate, t.endDate));
                    }
                    else
                    {
                        errorMessage += "'" + str_index + "' : " + GeneralConstant.ERROR_INVALID_INPUT_RANGE + "\n";
                    }
                }
                else
                {
                    errorMessage += "'" + str_index + "' : " + GeneralConstant.ERROR_INVALID_INPUT + "\n";
                }
            }
            return errorMessage;
        }

        private void updateAction()
        {
            List<Task> tasks = getAllTasks();
            if (objGUILogic.isError(tasks))
            {
                DisplayOutputOnInfoPanel(tasks);
                return;
            }

            string input = InputDialog.show(tasks, GeneralConstant.KEY_UPDATE);
            if (objGUILogic.isEmptyString(input))
            {
                return;
            }

            int index;
            string errorMessage = "";
            Task task = null;
            bool result = Int32.TryParse(input, out index);
            if (result)
            {
                if (objGUILogic.isIndexInRange(tasks, index))
                {
                    task = tasks[index - 1];
                }
                else
                {
                    errorMessage += "'" + input + "' : " + GeneralConstant.ERROR_INVALID_INPUT_RANGE + "\n";
                }
            }
            else
            {
                errorMessage += "'" + input + "' : " + GeneralConstant.ERROR_INVALID_INPUT + "\n";
            }

            if (!objGUILogic.isEmptyString(errorMessage))
            {
                objGUILogic.showErrorMessageBox(errorMessage);
            }
            else
            {
                string modifiedTaskName = InputDialog.show(GeneralConstant.INFO_UPDATE_INFORMATION, GeneralConstant.KEY_UPDATE);
                if (!objGUILogic.isEmptyString(modifiedTaskName))
                {
                    objKeywordProcessing = new KeywordProcessing();
                    List<Task> list_feedback;
                    List<Command> commandList = objKeywordProcessing.processKeyword(modifiedTaskName.Trim());
                    if (!objGUILogic.isError(commandList))
                    {
                        string updatetask = "update " + task.task + " to " + commandList[0].taskName + " on " + objGUILogic.getDate(task);
                        objKeywordProcessing = new KeywordProcessing();
                        List<Command> commandList2 = objKeywordProcessing.processKeyword(updatetask);
                        if (objGUILogic.isError(commandList2))
                        {
                            errorMessage = objGUILogic.getErrorMessage(commandList2);
                        }
                        else
                        {
                            list_feedback = objDataOperation.toDatabase(commandList2);
                            DisplayOutputOnInfoPanel(list_feedback);
                            if (!(commandList[0].startDate == DateTime.MinValue && commandList[0].endDate == DateTime.MinValue))
                            {                         
                                updatetask = "update " + objGUILogic.getDate(task) + " to " + objGUILogic.getDate(commandList[0].startDate, commandList[0].endDate) + " for " + commandList[0].taskName;
                                objKeywordProcessing = new KeywordProcessing();
                                List<Command> commandList3 = objKeywordProcessing.processKeyword(updatetask);
                                if (objGUILogic.isError(commandList3))
                                {
                                    errorMessage = objGUILogic.getErrorMessage(commandList3);
                                }
                                else
                                {
                                    list_feedback = objDataOperation.toDatabase(commandList3);
                                    DisplayOutputOnInfoPanel(list_feedback);
                                }
                            }

                        }
                    }
                }
                else
                {
                    errorMessage = GeneralConstant.INFO_NO_INPUT_FOUND;
                }
                objGUILogic.showErrorMessageBox(errorMessage);
            }
        }

    }
}
